package cc.unitmesh.agent.tool.schema

import cc.unitmesh.agent.logging.getLogger
import cc.unitmesh.agent.tool.ExecutableTool
import cc.unitmesh.agent.tool.ToolType
import cc.unitmesh.agent.tool.toToolType
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put

/**
 * Unified tool formatter for agent contexts
 *
 * This class provides consistent tool list formatting across all agent types
 * (Coding, CodeReview, etc.). It handles both built-in tools and MCP tools.
 */
object AgentToolFormatter {
    private val logger = getLogger("AgentToolFormatter")

    /**
     * Format tool list with enhanced schema information for AI understanding
     *
     * @param toolList List of executable tools to format
     * @return Formatted string containing tool descriptions and schemas
     */
    fun formatToolListForAI(toolList: List<ExecutableTool<*, *>>): String {
        logger.debug { "🔍 [AgentToolFormatter] Formatting tool list with ${toolList.size} tools:" }
        toolList.forEach { tool ->
            logger.debug { "  - ${tool.name} (${tool::class.simpleName}): ${tool.getParameterClass()}" }
        }

        if (toolList.isEmpty()) {
            logger.warn { "❌ [AgentToolFormatter] Tool list is empty" }
            return "No tools available."
        }

        return toolList.joinToString("\n\n") { tool ->
            buildString {
                appendLine("## ${tool.name}")
                val toolType = tool.name.toToolType()

                // Priority: 1. tool.metadata.schema (for tools like plan), 2. toolType.schema, 3. fallback
                val schema = tool.metadata.schema
                if (schema != null) {
                    // Use tool's own schema (e.g., PlanManagementSchema)
                    appendLine("**Parameters JSON Schema:**")
                    appendLine("```json")
                    val jsonSchema = schema.toJsonSchema()
                    val prettyJson = formatJsonSchema(jsonSchema)
                    appendLine(prettyJson)
                    appendLine("```")
                } else if (toolType != null) {
                    // Fallback to ToolType schema for backward compatibility
                    appendLine("**Parameters JSON Schema:**")
                    appendLine("```json")
                    val jsonSchema = toolType.schema.toJsonSchema()
                    val prettyJson = formatJsonSchema(jsonSchema)
                    appendLine(prettyJson)
                    appendLine("```")
                } else {
                    // No schema available, use parameter class info
                    val paramClass = tool.getParameterClass()
                    when {
                        paramClass.isBlank() || paramClass == "Unit" -> {
                            appendLine("**Parameters:** None")
                        }
                        paramClass == "AgentInput" -> {
                            // Generic agent input - provide more specific info for SubAgents
                            appendLine("**Parameters JSON Schema:**")
                            appendLine("```json")
                            appendLine("""{
  "${'$'}schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "description": "Generic agent input parameters",
  "additionalProperties": true
}""")
                            appendLine("```")
                        }
                        tool.name.contains("_") -> {
                            appendLine("**Parameters JSON Schema:**")
                            appendLine("```json")
                            appendLine("""{
  "${'$'}schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "description": "MCP tool parameters",
  "additionalProperties": true
}""")
                            appendLine("```")
                        }
                        else -> {
                            appendLine("**Parameters:** $paramClass")
                        }
                    }
                }

                val example = generateToolExample(tool, toolType)
                if (example.isNotEmpty()) {
                    appendLine("**Example:**")
                    appendLine(example)
                }
            }
        }
    }

    fun formatToolListSimple(toolList: List<ExecutableTool<*, *>>): String {
        return buildString {
            toolList.forEach { tool ->
                appendLine("- ${tool.name}: ${tool.description}")
            }
        }
    }

    /**
     * Format JSON schema as compact single line with $schema field
     */
    private fun formatJsonSchema(jsonElement: JsonElement): String {
        val jsonObject = jsonElement.jsonObject.toMutableMap()

        // Add $schema field if not present
        if (!jsonObject.containsKey("\$schema")) {
            jsonObject["\$schema"] = JsonPrimitive("http://json-schema.org/draft-07/schema#")
        }

        // Create a new JsonObject with $schema first
        val orderedJson = buildJsonObject {
            put("\$schema", jsonObject["\$schema"]!!)
            jsonObject.forEach { (key, value) ->
                if (key != "\$schema") {
                    put(key, value)
                }
            }
        }

        // Return compact JSON string
        return orderedJson.toString()
    }

    /**
     * Generate example usage for a tool with DevIns-style format (/command + JSON block)
     */
    private fun generateToolExample(tool: ExecutableTool<*, *>, toolType: ToolType?): String {
        return if (toolType != null) {
            // Generate DevIns-style example with JSON parameters
            generateDevInsExample(tool.name, toolType)
        } else {
            // Fallback for MCP tools or other tools
            when (tool.name) {
                "read-file" -> """/${tool.name}
```json
{"path": "src/main.kt", "startLine": 1, "endLine": 50}
```"""
                "write-file" -> """/${tool.name}
```json
{"path": "output.txt", "content": "Hello, World!"}
```"""
                "grep" -> """/${tool.name}
```json
{"pattern": "function.*main", "path": "src", "include": "*.kt"}
```"""
                "glob" -> """/${tool.name}
```json
{"pattern": "*.kt", "path": "src"}
```"""
                "shell" -> """/${tool.name}
```json
{"command": "ls -la"}
```"""
                else -> {
                    // For MCP tools or other tools, provide a generic example
                    if (tool.name.contains("_")) {
                        // Likely an MCP tool with server_toolname format
                        """/${tool.name}
```json
{"arguments": {"path": "/tmp"}}
```"""
                    } else {
                        """/${tool.name}
```json
{"parameter": "value"}
```"""
                    }
                }
            }
        }
    }

    /**
     * Generate DevIns-style example with JSON parameters based on schema
     */
    private fun generateDevInsExample(toolName: String, toolType: ToolType): String {
        val jsonSchema = toolType.schema.toJsonSchema()
        val properties = jsonSchema.jsonObject["properties"]?.jsonObject

        if (properties == null || properties.isEmpty()) {
            return """/$toolName
```json
{}
```"""
        }

        // Generate example JSON based on schema properties
        val exampleJson = buildJsonObject {
            properties.forEach { (paramName, paramSchema) ->
                val paramObj = paramSchema.jsonObject
                val type = paramObj["type"]?.jsonPrimitive?.content
                val defaultValue = paramObj["default"]

                when {
                    defaultValue != null -> put(paramName, defaultValue)
                    type == "string" -> {
                        val example = when (paramName) {
                            "path" -> "src/main.kt"
                            "content" -> "Hello, World!"
                            "pattern" -> "*.kt"
                            "command" -> "ls -la"
                            "message" -> "Example message"
                            else -> "example_value"
                        }
                        put(paramName, example)
                    }

                    type == "integer" -> {
                        val example = when (paramName) {
                            "startLine", "endLine" -> 1
                            "maxLines" -> 100
                            "port" -> 8080
                            else -> 42
                        }
                        put(paramName, example)
                    }

                    type == "boolean" -> put(paramName, false)
                    type == "array" -> put(paramName, buildJsonArray { add("example") })
                    else -> put(paramName, JsonPrimitive("example"))
                }
            }
        }

        return """/$toolName
```json
${exampleJson.toString()}
```"""
    }
}